/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
 */

package service

import (
	"encoding/base64"
	"mime/multipart"
	"path/filepath"
	"prim-server/common"
	"prim-server/config"
	"prim-server/dao"
	"prim-server/logger"
	"prim-server/model"
	"prim-server/util"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/spf13/cast"
)

const (
	//人像面
	IDCardPartOfHuman = 1
	//国徽面
	IDCardPartOfNation = 2
	//手持人像面
	IDCardPartOfHanding = 3
	//用户头像
	UserProfilePhoto = 4

	//上传文件路径连接符
	UploadFileConcatConnect = "/"

	//身份证图片格式限制为jpg(IVS接口限制),头像图片也限制为jpg
	UploadIDCardFileSuffix = ".jpg"
	//身份证图片格式限制base64大小不能超过4M(IVS接口限制)
	Base64MaxSize = 4 * 1024 * 1024 //4M
)

var (
	AdminUserName         = config.GetConfig().AdminConfig.Name
	AdminUserInitPassword = config.GetConfig().AdminConfig.Password
)

var UploadIDCardFileType map[int]bool
var UploadUserFileType map[int]bool

func init() {

	UploadIDCardFileType = make(map[int]bool)
	UploadUserFileType = make(map[int]bool)
	//上传身份证
	UploadIDCardFileType[IDCardPartOfHuman] = true
	UploadIDCardFileType[IDCardPartOfNation] = true
	UploadIDCardFileType[IDCardPartOfHanding] = true
	//上传用户头像
	UploadUserFileType[UserProfilePhoto] = true
}

// 构建文件上传路径
func buildUploadFilePath(fileType int, userId string) string {
	partString := []string{cast.ToString(userId), cast.ToString(fileType)}
	fileName := strings.Join(partString, UploadFileConcatConnect) + UploadIDCardFileSuffix
	return fileName
}

// 获取用户头像路径
func getUserProfilePhotoPath(userId string) string {
	return buildUploadFilePath(UserProfilePhoto, userId)
}

// 判断是否为上传身份证的文件类型
func ValidateFileTypeOfIDCard(fileType int) bool {
	if _, ok := UploadIDCardFileType[fileType]; ok {
		return true
	}
	return false
}

// 判断是否为上传用户头像等的相关的文件类型
func ValidateFileTypeOfUserFile(fileType int) bool {
	if _, ok := UploadUserFileType[fileType]; ok {
		return true
	}
	return false
}

// 判断是否为有效的上传文件类型--身份证获取用户头像
func validateFileType(fileType int) bool {
	if ValidateFileTypeOfIDCard(fileType) || ValidateFileTypeOfUserFile(fileType) {
		return true
	}
	return false
}

// 上传文件
func SaveFileWithPath(file []byte, fileType int, userId string) (string, error) {
	fileName := buildUploadFilePath(fileType, userId)
	finalKey := ""
	var err error
	if ValidateFileTypeOfIDCard(fileType) {
		//上传身份证图片到桶中
		finalKey, err = util.UploadIDCardFile(fileName, file)
	} else if ValidateFileTypeOfUserFile(fileType) {
		//上传用户头像到桶中
		finalKey, err = util.UploadProfilePhotoFile(fileName, file)
	} else {
		finalKey = ""
		err = errors.Errorf("文件上传类型不支持")
	}
	if err != nil {
		logger.Error("SaveFileWithPath error: %v fileType: %d, userId: %s", err.Error(), fileType, userId)
		return "", errors.Errorf("文件上传失败")
	}
	return finalKey, nil
}

// 对上传图片进行检查
func CheckUploadFile(information *multipart.FileHeader, maxSize int64) error {
	fileExt := strings.ToLower(filepath.Ext(information.Filename))
	if !strings.Contains(".jpg|.png|.bmp", fileExt) {
		return errors.Errorf("仅支持jpg,jpg,bmp格式")
	}
	if information.Size > maxSize {
		return errors.Errorf("文件大小不能超过2m")
	}
	return nil
}

// 检查身份证照片的base64字符串大小
func CheckIDCardByteBase64Size(fileBytes []byte, uploadFileType int) error {
	if ValidateFileTypeOfIDCard(uploadFileType) {
		base64Str := base64.StdEncoding.EncodeToString(fileBytes)
		strBytes := []byte(base64Str)
		if len(strBytes) > Base64MaxSize {
			logger.Error("CheckIDCardByteBase64Size: %v bytes", len(strBytes))
			return errors.Errorf("文件大小过大")
		}
	}
	return nil
}

// 创建系统账号和云上账号
func CreateUser(req common.RegistrationRequest, needUpdatePhone bool) error {
	resp := common.RegistrationResp{UserName: req.Name, PhoneNumber: ""}
	var err error
	if needUpdatePhone {
		err = CreatUser(req.Name, req.Password, req.PhoneNumber)
		resp.PhoneNumber = req.PhoneNumber
	} else {
		err = CreatUser(req.Name, req.Password, "")
	}
	if err != nil {
		logger.Error("create new user error: %v, userName: %s", err, req.Name)
		return err
	}
	userInfo, err := GetLoginUserInfo(req.Name)
	if err != nil {
		logger.Error("get user error: %v, userName: %s", err, req.Name)
		return err
	}
	//云上创建用户
	err = CreateCloudUser(userInfo.Id, userInfo.Name)
	if err != nil {
		logger.Error("create cloud user error: %v, userId: %s", err, userInfo.Id)
		return err
	}
	return nil
}

// 管理员相关
// InitAdminUser 系统初始化时，初始化管理员账号
func InitAdminUser() error {
	user, err := GetAdmin()
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	if user != nil {
		return nil
	}
	salt, encryptedPass, err := util.EncryptPassword(AdminUserInitPassword, common.Iter, common.KeyLength)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	admin := &model.User{
		Id:            util.GetUUID32(),
		Name:          AdminUserName,
		Password:      encryptedPass,
		Salt:          salt,
		FailTimes:     0,
		Locked:        false,
		LockedTime:    time.Time{},
		CreatedTime:   time.Now(),
		UpdateTime:    time.Now(),
		PwdChanged:    false,
		Role:          AdminRole,
		Authenticated: true,
	}
	if err := dao.GetUserDao().SaveUserInfo(admin); err != nil {
		logger.Error("init admin error: %v", err.Error())
		return errors.Errorf("创建管理员失败")
	}
	return nil
}
func GetAdmin() (*model.User, error) {
	queryParameter := &common.QueryUserDetail{
		UserName: []string{AdminUserName},
	}
	users, _, err := GetUserAllWithQueryParameter(queryParameter)
	if err != nil {
		logger.Error("get admin error: %v", err.Error())
		return nil, err
	}
	if len(users) == 0 {
		return nil, nil
	}
	admin := users[0]
	return admin, nil
}
func GetAdminId() (string, error) {
	admin, err := GetAdmin()
	if err != nil {
		logger.Error("get admin error: %v", err.Error())
		return "", err
	}
	return admin.Id, nil
}

func JudgeAdminUser(userId string) error {
	admin, err := GetAdmin()
	if err != nil {
		return err
	}
	if userId == admin.Id {
		return errors.New("当前用户为管理员")
	}
	return nil
}

// 查找用户
func GetUserAllWithQueryParameter(queryParameter *common.QueryUserDetail) ([]*model.User, int, error) {
	if queryParameter == nil {
		return nil, 0, nil
	}
	users, totalCount, err := dao.GetUserDao().GetUserInfo(queryParameter)
	//已处理不存在的情况
	if err != nil {
		logger.Error(err.Error())
		return nil, 0, errors.New("查找用户失败")
	}
	return users, totalCount, nil
}
func GetUserAll(userId, userName, phoneNumber []string, locked, pwdChanged []bool, page, pageSize *int) ([]*model.User, int, error) {
	queryParameter := &common.QueryUserDetail{
		UserId:      userId,
		UserName:    userName,
		PhoneNumber: phoneNumber,
		Locked:      locked,
		PwdChanged:  pwdChanged,
		Page:        page,
		PageSize:    pageSize,
	}
	return GetUserAllWithQueryParameter(queryParameter)
}

func GetUserOneWithUserId(userId string) (*model.User, error) {
	users, _, err := GetUserAll([]string{userId}, nil, nil, nil, nil, nil, nil)
	//error has been handle,return directly
	if err != nil {
		logger.Error("get user error: %v, userId: %s", err.Error(), userId)
		return nil, err
	}
	if len(users) == 0 {
		return nil, nil
	}
	return users[0], nil
}

func GetUserOneWithUserName(userName string) (*model.User, error) {
	users, _, err := GetUserAll(nil, []string{userName}, nil, nil, nil, nil, nil)
	//error has been handle,return directly
	if err != nil {
		logger.Error("get user error: %v, userName: %s", err.Error(), userName)
		return nil, err
	}
	if len(users) == 0 {
		return nil, nil
	}
	return users[0], nil
}
func GetUserOneWithPhoneNumber(phoneNumber string) (*model.User, error) {
	users, _, err := GetUserAll(nil, nil, []string{phoneNumber}, nil, nil, nil, nil)
	//error has been handle,return directly
	if err != nil {
		logger.Error("get user error: %v, phone: %s", err.Error(), phoneNumber)
		return nil, err
	}
	if len(users) == 0 {
		return nil, nil
	}
	return users[0], nil
}

// GetLoginUserInfo 根据用户名获取用户的信息
func GetLoginUserInfo(userName string) (*common.LoginUserInfo, error) {
	user, err := GetUserOneWithUserName(userName)
	//error has been handle,return directly
	if err != nil {
		logger.Error("get user error: %v, userName: %s", err.Error(), userName)
		return nil, err
	}
	if user == nil {
		return nil, nil
	}
	loginUserInfo := &common.LoginUserInfo{
		Id:            user.Id,
		Name:          user.Name,
		Password:      user.Password,
		Salt:          user.Salt,
		FailTimes:     user.FailTimes,
		Locked:        user.Locked,
		LockedTime:    user.LockedTime,
		Role:          user.Role.Name,
		PwdChanged:    user.PwdChanged,
		Authenticated: user.Authenticated,
	}
	return loginUserInfo, nil
}

func GetUserInfoByPhoneNum(phoneNum string) (*common.LoginUserInfo, error) {
	user, err := GetUserOneWithPhoneNumber(phoneNum)
	// 查询数据库出错
	if err != nil {
		logger.Error("get photo error: %v, phone: %s", err.Error(), phoneNum)
		return nil, err
	}
	loginUserInfo := &common.LoginUserInfo{
		Id:            user.Id,
		Name:          user.Name,
		Password:      user.Password,
		Salt:          user.Salt,
		FailTimes:     user.FailTimes,
		Locked:        user.Locked,
		LockedTime:    user.LockedTime,
		Role:          user.Role.Name,
		PwdChanged:    user.PwdChanged,
		Authenticated: user.Authenticated,
	}
	return loginUserInfo, nil
}

// 更新用户头像--首次上传
func UpdateProfilePhotoFirst(userId string) error {
	user, err := GetUserOneWithUserId(userId)
	if err != nil {
		logger.Error("get user error: %v, userId: %s", err.Error(), userId)
		return errors.New("查找用户头像失败")
	}
	//由于用户头像路径固定，所以非空时才做第一次更新
	if len(user.ProfilePhotoUrl) == 0 {
		profilePhotoUrl := getUserProfilePhotoPath(userId)
		err := updateProfilePhoto(userId, profilePhotoUrl)
		if err != nil {
			logger.Error("get photo error: %v, userId: %s", err.Error(), userId)
			return errors.New("更新用户头像失败")
		}
	}
	return nil
}
func updateProfilePhoto(userId string, profilePhotoUrl string) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:          userId,
		ProfilePhotoUrl: &profilePhotoUrl,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		logger.Error("update user error: %v, userId: %s, photo: %s", err.Error(), userId, profilePhotoUrl)
		return err
	}
	return nil
}

// GetUserProfile 用户个人信息页面，根据用户名获取用户的个人信息
func GetUserProfile(userId string) (*common.Profile, error) {
	user, err := GetUserOneWithUserId(userId)
	// 查询数据库出错
	if err != nil {
		logger.Error("get user error: %v, userId: %s", err.Error(), userId)
		return nil, errors.New("查找用户信息失败")
	}
	regPhoneNum := ""
	if user.PhoneNumber != "" && len(user.PhoneNumber) == util.PhoneNumLen {
		regPhoneNum = user.PhoneNumber[0:3]
		regPhoneNum += "****"
		regPhoneNum += user.PhoneNumber[7:11]
	}
	ProfilePhotoUrl := ""
	if user.ProfilePhotoUrl != "" {
		ProfilePhotoUrl, err = util.GetFileUrl(user.ProfilePhotoUrl)
		if err != nil {
			return nil, errors.New("获取用户头像失败")
		}
	}
	userProfile := &common.Profile{
		Name:            user.Name,
		CreatedTime:     user.CreatedTime.UnixMilli(),
		Authenticated:   user.Authenticated,
		RegPhoneNum:     regPhoneNum,
		ProfilePhotoUrl: ProfilePhotoUrl,
	}
	return userProfile, nil
}

// 删除用户
func deleteUserWithUserId(userId string) error {
	if err := dao.GetUserDao().DeleteUserInfo(userId); err != nil {
		logger.Error("delete user error: %v, userId: %s", err.Error(), userId)
		return errors.Errorf("删除用户失败")
	}
	return nil
}

// Unsubscribe 根据用户id注销用户
func Unsubscribe(userId string) error {
	if err := deleteUserWithUserId(userId); err != nil {
		logger.Error("unsubscribe error: %v, userId: %s", err.Error(), userId)
		return errors.New("用户注销失败")
	}
	return nil
}

// CreatUser 用户注册时，通过用户名和密码创建新用户
func CreatUser(username, password, phoneNum string) error {
	// 生成salt对密码进行加密
	salt, encryptPass, err := util.EncryptPassword(password, common.Iter, common.KeyLength)
	if err != nil {
		return err
	}
	user := &model.User{
		Id:            util.GetUUID32(),
		Name:          username,
		Password:      encryptPass,
		Salt:          salt,
		PhoneNumber:   phoneNum,
		FailTimes:     0,
		Locked:        false,
		LockedTime:    time.Time{},
		CreatedTime:   time.Now(),
		UpdateTime:    time.Now(),
		PwdChanged:    false,
		Role:          UserRole,
		Authenticated: false,
	}
	err = dao.GetUserDao().SaveUserInfo(user)
	if err != nil {
		return errors.Errorf("SaveUserInfo error: %v", err)
	}
	return nil
}
func UpdatePassword(userId string, password, salt string) error {
	updateUserParameters := common.ModifyUserDetail{
		UserId:   userId,
		Password: &password,
		Salt:     &salt,
	}
	err := dao.GetUserDao().UpdateAllUserInfo(&updateUserParameters)
	if err != nil {
		return err
	}
	return nil
}

// 获取用户数量
func GetUserNum() (int, error) {
	return GetUserNumByNameAndStatus(nil, nil)
}

// GetUserNumByMatchingName  根据用户名模糊匹配分页获取用户的数量
func GetUserNumByMatchingName(name string) (int, error) {
	return GetUserNumByNameAndStatus(&name, nil)
}

// GetUserNumByStatus 根据用户状态获取用户数量
func GetUserNumByStatus(locked bool) (int, error) {
	return GetUserNumByNameAndStatus(nil, &locked)
}

// GetUserNumByNameAndStatus 根据用户名和用户状态获取用户数量
func GetUserNumByNameAndStatus(name *string, locked *bool) (int, error) {
	queryParameter := common.QueryUserDetail{}
	if name != nil {
		queryParameter.UserName = []string{*name}
	}
	if locked != nil {
		queryParameter.Locked = []bool{*locked}
	}
	_, totalCount, err := GetUserAllWithQueryParameter(&queryParameter)
	if err != nil {
		logger.Error(err.Error())
		return 0, err
	}
	return totalCount, nil
}

// GetUsers 分页获取所有的用户
func GetUsers(pageSize, page int) ([]*common.User, error) {
	return GetUsersByNameAndStatus(nil, nil, &page, &pageSize)
}

// GetUsersByMatchingName  根据用户名模糊匹配分页获取所有用户
func GetUsersByMatchingName(userName string, page, pageSize int) ([]*common.User, error) {
	return GetUsersByNameAndStatus(&userName, nil, &page, &pageSize)
}

// GetUsersByStatus 根据用户是否被锁定分页获取用户列表
func GetUsersByStatus(locked bool, page, pageSize int) ([]*common.User, error) {
	return GetUsersByNameAndStatus(nil, &locked, &page, &pageSize)
}

// GetUsersByNameAndStatus 根据用户名和用户状态获取用户列表
func GetUsersByNameAndStatus(userName *string, locked *bool, page, pageSize *int) ([]*common.User, error) {
	queryParameter := &common.QueryUserDetail{
		Page:     page,
		PageSize: pageSize,
	}
	if userName != nil {
		queryParameter.UserName = []string{*userName}
	}
	if locked != nil {
		queryParameter.Locked = []bool{*locked}
	}
	userModels, _, err := GetUserAllWithQueryParameter(queryParameter)
	if err != nil {
		logger.Error("get user error: %v", err.Error())
		return nil, err
	}
	var users []*common.User
	for _, value := range userModels {
		tmpUser := &common.User{
			UserId:        value.Id,
			UserName:      value.Name,
			Locked:        value.Locked,
			Role:          value.Role.Name,
			Authenticated: value.Authenticated,
		}
		users = append(users, tmpUser)
	}
	return users, nil
}

// 根据用户ID获取信息
func GetUserById(userId string) (*common.User, error) {
	user, err := GetUserOneWithUserId(userId)
	if err != nil {
		logger.Error("get user error: %v, userId: %s", err.Error(), userId)
		return nil, err
	}
	if user == nil {
		return nil, nil
	}
	res := &common.User{
		UserId:        user.Id,
		UserName:      user.Name,
		Locked:        user.Locked,
		Role:          user.Role.Name,
		Authenticated: user.Authenticated,
	}
	return res, nil
}

// 删除用户
func DeleteUserById(userId string) error {
	err := dao.GetUserDao().DeleteUserInfo(userId)
	if err != nil {
		logger.Error("delete user error: %v, userId: %s", err.Error(), userId)
		return errors.New("删除用户失败")
	}
	return nil
}

// 更新用户信息
func UpdateUserInfo(userId string, locked *bool, lockTime *time.Time, failTimes *int, authenticated *bool) error {
	updateUserParameter := &common.ModifyUserDetail{
		UserId: userId,
	}
	if locked != nil {
		updateUserParameter.Lock = locked
	}
	if lockTime != nil {
		t := util.GetMilliSec(*lockTime)
		updateUserParameter.LockTime = &t
	}
	if failTimes != nil {
		updateUserParameter.FailTimes = failTimes
	}
	if authenticated != nil {
		updateUserParameter.Authenticated = authenticated
	}
	err := dao.GetUserDao().UpdateAllUserInfo(updateUserParameter)
	if err != nil {
		logger.Error(err.Error())
		return errors.New("更新用户信息失败")
	}
	return nil
}
func UpdateLockedById(userId string, locked bool) (*common.User, error) {
	err := UpdateUserInfo(userId, &locked, nil, nil, nil)
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	user, err := GetUserById(userId)
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	return user, nil
}

func UpdateLockedTime(userId string, lockTime time.Time) error {
	err := UpdateUserInfo(userId, nil, &lockTime, nil, nil)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	return nil
}
func UpdateFailTimes(userId string, failTimes int) error {
	err := UpdateUserInfo(userId, nil, nil, &failTimes, nil)
	if err != nil {
		logger.Error(err.Error())
		return err
	}
	return nil
}
func UpdateAuthentication(userId string, authenticated bool) error {
	err := UpdateUserInfo(userId, nil, nil, nil, &authenticated)
	if err != nil {
		logger.Error("UpdateAuthentication error: %v, userId: %d, authenticated: %v", err.Error(), userId, authenticated)
		return err
	}
	return nil
}

func FindAuthenticationInfo(userId string) (bool, error) {
	var err error
	if userId == "" {
		logger.Error("userId is empty")
		return false, errors.New("用户ID为空")
	}
	userInfo, err := GetUserById(userId)
	if err != nil {
		logger.Error("get user error: %v, userId: %s", err.Error(), userId)
		return false, errors.New("查询用户失败")
	}
	if userInfo == nil {
		logger.Error("userId can't be found, userId: %s", err.Error(), userId)
		return false, errors.New("查询用户ID不存在")
	}
	userAuthenticatedInfo := userInfo.Authenticated
	if userAuthenticatedInfo {
		return true, nil
	} else {
		return false, nil
	}
}
